package cn.xo68.ide.plugin.common.git;

import cn.xo68.core.util.StringTools;
import com.jcraft.jsch.Session;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.*;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.Set;

@SuppressWarnings("restriction")
public class GitClient implements Closeable {

    private static final Logger logger = LoggerFactory.getLogger(GitClient.class);

    private String remotePath;
    private File localDirectory = null;
    private File gitDirectory = null;
    private Repository localRepo=null;
    private Git git;
    
    private String authorName;
    private String authorEmail;
    private String commiterName;
    private String commiterEmail;


    public void setRemotePath(String remotePath) {
        this.remotePath = remotePath;
    }

    public void setLocalDirectory(File localDirectory) {
        this.localDirectory = localDirectory;
    }

    public String getAuthorName() {
        return authorName;
    }
    public void setAuthorName(String authorName) {
        this.authorName = authorName;
    }
    
    public String getAuthorEmail() {
        return authorEmail;
    }
    public void setAuthorEmail(String authorEmail) {
        this.authorEmail = authorEmail;
    }

    public String getCommiterName() {
        return commiterName;
    }
    public void setCommiterName(String commiterName) {
        this.commiterName = commiterName;
    }



    public String getCommiterEmail() {
        return commiterEmail;
    }



    public void setCommiterEmail(String commiterEmail) {
        this.commiterEmail = commiterEmail;
    }



    public void init() throws IOException {
        gitDirectory = new File(localDirectory, ".git");
        if (!gitDirectory.getParentFile().exists()) {
            gitDirectory.getParentFile().mkdirs();
        }
        localRepo = new FileRepository(gitDirectory);
        git = new Git(localRepo);

        SshSessionFactory.setInstance(new JschConfigSessionFactory() {
            @Override
            protected void configure(OpenSshConfig.Host hc, Session session) {
                session.setConfig("StrictHostKeyChecking", "no");
            }
        });
    }


    public GitClient(){

    }

    public GitClient(File localDirectory, String remotePath) {
        super();
        this.localDirectory = localDirectory;
        this.remotePath = remotePath;

    }

    public Boolean localRepositoryExist() {
        return gitDirectory.exists();
    }


    public void createLocalRepository() throws IOException {
        // Repository newRepo = new FileRepository(localPath + "/.git");        
        localRepo.create(true);
    }

    public void cloneRepository(String branch,String userName,String password) throws Exception {
        CloneCommand cc = null;
        if (StringTools.isEmpty(branch)) {
            branch="master";
        } 
        cc = Git.cloneRepository().setURI(remotePath).setBranch(branch);
        try {
            if(StringTools.isEmpty(userName) || StringTools.isEmpty(password)) {
                cc.setDirectory(localDirectory)
                .call();
            }else {
                UsernamePasswordCredentialsProvider usernamePasswordCredentialsProvider =new UsernamePasswordCredentialsProvider(userName,password);
                cc.setDirectory(localDirectory)
                .setCredentialsProvider(usernamePasswordCredentialsProvider)
                .call();
            }
            
        } catch (InvalidRemoteException e) {
            throw new Exception(e);
        } catch (TransportException e) {
            throw new Exception(e);
        } catch (GitAPIException e) {
            throw new Exception(e);
        }
    }

    public void pull(String branch,String userName,String password)  throws Exception{
        if (StringTools.isEmpty(branch)) {
            branch="master";
        } 
        
        try {
            if(StringTools.isEmpty(userName) || StringTools.isEmpty(password)) {
                git.pull()
                .setRemoteBranchName(branch)
                .call();
            }else {
                UsernamePasswordCredentialsProvider usernamePasswordCredentialsProvider =new UsernamePasswordCredentialsProvider(userName,password);
                git.pull()
                .setRemoteBranchName(branch)
                .setCredentialsProvider(usernamePasswordCredentialsProvider)
                .call();
            }
            
        } catch (WrongRepositoryStateException e) {
            throw new Exception(e);
        } catch (InvalidConfigurationException e) {
            throw new Exception(e);
        } catch (InvalidRemoteException e) {
            throw new Exception(e);
        } catch (CanceledException e) {
            throw new Exception(e);
        } catch (RefNotFoundException e) {
            throw new Exception(e);
        } catch (RefNotAdvertisedException e) {
            throw new Exception(e);
        } catch (NoHeadException e) {
            throw new Exception(e);
        } catch (TransportException e) {
            throw new Exception(e);
        } catch (GitAPIException e) {
            throw new Exception(e);
        }


    }

    public void add(String...filePatterns) throws Exception {
        if(filePatterns==null || filePatterns.length<1) {
            return;
        }
        try {
            AddCommand addCommand = git.add();
            for (String filePattern : filePatterns) {
                addCommand.addFilepattern(filePattern);
            }
            addCommand.call();
        } catch (NoFilepatternException e) {
            throw new Exception(e);
        } catch (GitAPIException e) {
            throw new Exception(e);
        }
    }

    public void commit(String commitRemark,String...filePatterns)  throws Exception{
        try {
            CommitCommand commitCommand = git.commit();
            
            if(filePatterns!=null && filePatterns.length>0) {
                for (String filePattern : filePatterns) {
                    commitCommand.setOnly(filePattern);
                }
            }
            
            if(StringTools.isNotEmpty(authorName) && StringTools.isNotEmpty(authorEmail)) {
                commitCommand.setAuthor(authorName, authorEmail);
            }
            if(StringTools.isNotEmpty(commiterName) && StringTools.isNotEmpty(commiterEmail)) {
                commitCommand.setAuthor(commiterName, commiterEmail);
            }
            
            commitCommand.setMessage(commitRemark).call();
        } catch (NoHeadException e) {
            throw e;
        } catch (NoMessageException e) {
            throw e;
        } catch (UnmergedPathsException e) {
            throw e;
        } catch (ConcurrentRefUpdateException e) {
            throw e;
        } catch (WrongRepositoryStateException e) {
            throw e;
        } catch (AbortedByHookException e) {
            throw e;
        } catch (GitAPIException e) {
            throw e;
        }
    }

    public void push(String branch,String userName,String password,Boolean force) throws Exception {
        UsernamePasswordCredentialsProvider usernamePasswordCredentialsProvider =new UsernamePasswordCredentialsProvider(userName,password);
        try {
          Iterable<PushResult> results = git.push()
            .setRemote("origin")
            .setForce(force)
            .setCredentialsProvider(usernamePasswordCredentialsProvider)
            .call();
          for (PushResult pushResult : results) {
			logger.info("push result: {}",pushResult.getURI().getRawPath());
		  }
        } catch (InvalidRemoteException e) {
           throw e;
        } catch (TransportException e) {
            throw e;
        } catch (GitAPIException e) {
            throw e;
        }
        
        
    }
    
    public Status getStatus() throws Exception{
        Status status;
        try {
            status = git.status().call();
        } catch (NoWorkTreeException e) {
            throw e;
        } catch (GitAPIException e) {
            throw e;
        }
        return status;
    }
    
    public GitElementList getAdded(){
        Set<String> sets=null;
        try {
            sets = getStatus().getAdded();
        } catch (Exception e) {
            e.printStackTrace();
        }
        GitElementList list=new GitElementList();
        list.add(GitElementStatus.Untracked, sets);    
        return list;
    }
    public GitElementList getRemoved(){
        Set<String> sets=null;
        try {
            sets = getStatus().getRemoved();
        } catch (Exception e) {
            e.printStackTrace();
        }
        GitElementList list=new GitElementList();
        list.add(GitElementStatus.Untracked, sets);    
        return list;
    }
    
    /**
     * 未跟踪的文件，即没有控制的文件
     * @return
     * @throws Exception
     */
    public GitElementList getUnTracked(){
        Set<String> sets=null;
        try {
            sets = getStatus().getUntracked();
        } catch (Exception e) {
            e.printStackTrace();
        }
        GitElementList list=new GitElementList();
        list.add(GitElementStatus.Untracked, sets);    
        return list;
    }
    
    public GitElementList getModified(){
        Set<String> sets=null;
        try {
            sets = getStatus().getModified();
        } catch (Exception e) {
            e.printStackTrace();
        }
        GitElementList list=new GitElementList();
        list.add(GitElementStatus.Modified, sets);    
        return list;
    }
    public GitElementList getMissing(){
        Set<String> sets=null;
        try {
            sets = getStatus().getMissing();
        } catch (Exception e) {
            e.printStackTrace();
        }
        GitElementList list=new GitElementList();
        list.add(GitElementStatus.Missing, sets);
        return list;
    }
    public GitElementList getWaitCommit(){
        GitElementList waitCommitFiles =new GitElementList();
        GitElementList unTracked = this.getUnTracked();
        waitCommitFiles.addAll(unTracked);
        GitElementList modified = this.getModified();
        waitCommitFiles.addAll(modified);
        GitElementList missing = this.getMissing();
        waitCommitFiles.addAll(missing);
        
        GitElementList added = this.getAdded();
        waitCommitFiles.addAll(added);
        
        GitElementList removed = this.getRemoved();
        waitCommitFiles.addAll(removed);
        return waitCommitFiles;
    }
    

    @Override
    public void close() throws IOException {
        if(localRepo!=null) {
            localRepo.close();
            localRepo=null;
        }
        if(git!=null) {
            git.close();
            git=null;
        }
    }
}
